import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.font.TextAttribute;
import java.util.Hashtable;
import java.util.StringTokenizer;


public class Options
{
	private int planetType; // 0 - off, 1 - invisible, 2 - visible
	private boolean unlimitedLives; // false means 3 lives
	private int startingLevel; // level on new game
	private int numAsteroids; // asteroids on level 1; 0 - asteroids (and levels) off entirely
	private boolean aliens; // true: aliens generated randomly
	private boolean playerTwo; // true: player two exists, score shown, etc
	
	static Font titleFont = new Font(Font.SANS_SERIF, Font.BOLD, 40); // "OPTIONS"
	static Font nameFont = new Font(Font.SANS_SERIF, Font.BOLD, 18); // "Planet:" etc
	static Font selectedFont = new Font(Font.SANS_SERIF, Font.BOLD, 16); // currently selected options
	static Font optionFont = new Font(Font.SANS_SERIF, Font.PLAIN, 16); // currently unselected options
	static Font textFont = new Font(Font.SANS_SERIF, Font.PLAIN, 12);
	
	// 0: planet
	// 1: lives
	// 2: start level
	// 3: asteroids
	// 4: aliens
	// 5: players
	// 6: continue/save/load/quit
	// 7: reset scores
	private int cursorRow = 0;
	
	// only relevant for the continue/save/quit row
	private int cursorCol = 0;
	
	// set if we need to start a new game upon exiting the options
	private boolean needNew;
	
	public Options()
	{
		setDefaults();
		startOptions();
		
		// set up selectedFont to have underlining
		Hashtable<TextAttribute, Object> map = new Hashtable<TextAttribute, Object>();
		map.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
        selectedFont = selectedFont.deriveFont(map);
	}
	
	protected String saveState()
	{
		return String.format("%d %s %d %d %s %s\n",
				planetType, unlimitedLives, startingLevel, numAsteroids, aliens, playerTwo);
	}
	protected void loadState(StringTokenizer strtok)
	{
		planetType = Integer.parseInt(strtok.nextToken());
		unlimitedLives = Boolean.parseBoolean(strtok.nextToken());
		startingLevel = Integer.parseInt(strtok.nextToken());
		numAsteroids = Integer.parseInt(strtok.nextToken());
		aliens = Boolean.parseBoolean(strtok.nextToken());
		playerTwo = Boolean.parseBoolean(strtok.nextToken());
	}
	
	private void setDefaults()
	{
		planetType = 0;
		unlimitedLives = false;
		startingLevel = 1;
		numAsteroids = 3;
		aliens = true;
		playerTwo = false;
	}
	
	public boolean planetOn() { return planetType != 0; }
	public boolean planetVisible() { return planetType == 2; }
	public boolean unlimitedLives() { return unlimitedLives; }
	public int startLevel() { return startingLevel; }
	public int level1Asteroids() { return numAsteroids; }
	public boolean aliensOn() { return aliens; }
	public boolean playerTwo() { return playerTwo; }
	
	public void drawOptionsMenu(int boxX, int boxY, Graphics g)
	{
		Graphics2D g1 = (Graphics2D) g.create(); // make a copy of the graphics so we can transform it
		g1.translate(boxX,boxY);
		
		int titleHeight = g1.getFontMetrics(titleFont).getHeight();
		int titleWidth = g1.getFontMetrics(titleFont).stringWidth("OPTIONS");
		
		int rowHeight = g1.getFontMetrics(nameFont).getHeight();
		int textRowHeight = g1.getFontMetrics(textFont).getHeight();
		
		int boxHeight = 50 + titleHeight + 13*rowHeight + 4*textRowHeight;
		int boxWidth = 500; // just an approximation
		
		g1.setColor(Color.BLACK);
		g1.fillRect(-boxWidth/2, -boxHeight/2, boxWidth, boxHeight);
		g1.setColor(Color.WHITE);
		g1.drawRect(-boxWidth/2, -boxHeight/2, boxWidth, boxHeight);
		
		// go back to top left of box
		g1.translate(-boxWidth/2, -boxHeight/2);
		
		int y = 0;
		g1.setFont(titleFont);
		g1.drawString("OPTIONS", boxWidth/2 - titleWidth/2, y + titleHeight);
		y += titleHeight;
		
		g1.setColor(cursorRow == 0? Color.YELLOW : Color.WHITE);
		int x = 20;
		x += drawOptionName(x, y, "Planet:", g1) + 20;
		x += drawOption(x, y, "off", planetType == 0, g1) + 10;
		x += drawOption(x, y, "invisible", planetType == 1, g1) + 10;
		x += drawOption(x, y, "visible", planetType == 2, g1) + 10;
		y += rowHeight;
		
		g1.setColor(cursorRow == 1? Color.YELLOW : Color.WHITE);
		x = 20;
		x += drawOptionName(x, y, "Lives:", g1) + 20;
		x += drawOption(x, y, "3", !unlimitedLives, g1) + 10;
		x += drawOption(x, y, "unlimited", unlimitedLives, g1) + 10;
		y += rowHeight;
		
		g1.setColor(cursorRow == 2? Color.YELLOW : Color.WHITE);
		x = 20;
		x += drawOptionName(x, y, "Starting Level:", g1) + 20;
		x += drawOption(x, y, String.format("%d", startingLevel), true, g1) + 10;
		y += rowHeight;
		
		g1.setColor(cursorRow == 3? Color.YELLOW : Color.WHITE);
		x = 20;
		x += drawOptionName(x, y, "Asteroids:", g1) + 20;
		x += drawOption(x, y, "off", numAsteroids == 0, g1) + 10;
		x += drawOption(x, y, "1", numAsteroids == 1, g1) + 10;
		x += drawOption(x, y, "3", numAsteroids == 3, g1) + 10;
		x += drawOption(x, y, "5", numAsteroids == 5, g1) + 10;
		x += drawOption(x, y, "10", numAsteroids == 10, g1) + 10;
		y += rowHeight;
		
		g1.setColor(cursorRow == 4? Color.YELLOW : Color.WHITE);
		x = 20;
		x += drawOptionName(x, y, "Aliens:", g1) + 20;
		x += drawOption(x, y, "off", !aliens, g1) + 10;
		x += drawOption(x, y, "on", aliens, g1) + 10;
		y += rowHeight;
		
		g1.setColor(cursorRow == 5? Color.YELLOW : Color.WHITE);
		x = 20;
		x += drawOptionName(x, y, "Players:", g1) + 20;
		x += drawOption(x, y, "1", !playerTwo, g1) + 10;
		x += drawOption(x, y, "2", playerTwo, g1) + 10;
		y += rowHeight;
		
		g1.setColor(needNew? Color.YELLOW : Color.WHITE);
		x = 20;
		g1.setFont(textFont);
		g1.drawString("Changing options will end the current game.", x, y + rowHeight);
		y += rowHeight;
		
		y += rowHeight;
		
		g1.setColor(cursorRow == 6? Color.YELLOW : Color.WHITE);
		x = 20;
		x += drawOption(x, y, "Continue", cursorRow == 6 && cursorCol == 0, g1) + 10;
		x += drawOption(x, y, "Save", cursorRow == 6 && cursorCol == 1, g1) + 10;
		x += drawOption(x, y, "Load", cursorRow == 6 && cursorCol == 2, g1) + 10;
		x += drawOption(x, y, "Quit", cursorRow == 6 && cursorCol == 3, g1) + 10;
		y += rowHeight;
		
		y += rowHeight;
		
		g1.setColor(cursorRow == 7? Color.YELLOW : Color.WHITE);
		x = 20;
		x += drawOption(x, y, "Reset High Scores", cursorRow == 7, g1) + 10;
		y += rowHeight;
		
		y += rowHeight;
		
		g1.setColor(Color.WHITE);
		x = 20;
		x += drawOptionName(x, y, "Controls", g1) + 10;
		y += rowHeight;
		
		drawText(30, y, "Player 1", g1);
		drawText(30 + boxWidth/3, y, "Player 2", g1);
		y += textRowHeight;
		
		drawText(40, y, "UP - accelerate", g1);
		drawText(40 + boxWidth/3, y, "W - accelerate", g1);
		drawText(40 + boxWidth*2/3, y, "P - pause/resume", g1);
		y += textRowHeight;
		
		drawText(40, y, "DOWN - brake", g1);
		drawText(40 + boxWidth/3, y, "S - brake", g1);
		drawText(40 + boxWidth*2/3, y, "ESC - options", g1);
		y += textRowHeight;
		
		drawText(40, y, "LEFT/RIGHT - turn", g1);
		drawText(40 + boxWidth/3, y, "A/D - turn", g1);
		y += textRowHeight;
		
		drawText(40, y, "SHIFT - shoot", g1);
		drawText(40 + boxWidth/3, y, "E - shoot", g1);
		y += textRowHeight;

	}
	
	// set cursor to the continue option to start
	public void startOptions()
	{
		cursorRow = 6;
		cursorCol = 0;
		needNew = false;
	}
	
	public void keyPress(KeyEvent e)
	{
		if(e.getKeyCode() == KeyEvent.VK_UP)
		{
			cursorRow -= 1;
			if(cursorRow < 0)
				cursorRow = 7;
		}
		else if(e.getKeyCode() == KeyEvent.VK_DOWN)
		{
			cursorRow += 1;
			if(cursorRow > 7)
				cursorRow = 0;
		}
		else if(e.getKeyCode() == KeyEvent.VK_LEFT)
		{
			switch(cursorRow)
			{
			case 0:
				if(planetType > 0)
					planetType -= 1;
				needNew = true;
				break;
			case 1:
				if(unlimitedLives)
					unlimitedLives = false;
				needNew = true;
				break;
			case 2:
				if(startingLevel > 1)
					startingLevel -= 1;
				needNew = true;
				break;
			case 3:
				if(numAsteroids == 10)
					numAsteroids = 5;
				else if(numAsteroids == 5)
					numAsteroids = 3;
				else if(numAsteroids == 3)
					numAsteroids = 1;
				else if(numAsteroids == 1)
					numAsteroids = 0;
				needNew = true;
				break;
			case 4:
				if(aliens)
					aliens = false;
				needNew = true;
				break;
			case 5:
				if(playerTwo)
					playerTwo = false;
				needNew = true;
				break;
			case 6:
				if(cursorCol > 0)
					cursorCol -= 1;
				break;
			case 7:
				break;
			}
		}
		else if(e.getKeyCode() == KeyEvent.VK_RIGHT)
		{
			switch(cursorRow)
			{
			case 0:
				if(planetType < 2)
					planetType += 1;
				needNew = true;
				break;
			case 1:
				if(!unlimitedLives)
					unlimitedLives = true;
				needNew = true;
				break;
			case 2:
				if(startingLevel < 20)
					startingLevel += 1;
				needNew = true;
				break;
			case 3:
				if(numAsteroids == 5)
					numAsteroids = 10;
				else if(numAsteroids == 3)
					numAsteroids = 5;
				else if(numAsteroids == 1)
					numAsteroids = 3;
				else if(numAsteroids == 0)
					numAsteroids = 1;
				needNew = true;
				break;
			case 4:
				if(!aliens)
					aliens = true;
				needNew = true;
				break;
			case 5:
				if(!playerTwo)
					playerTwo = true;
				needNew = true;
				break;
			case 6:
				if(cursorCol < 3)
					cursorCol += 1;
				break;
			case 7:
				break;
			}
		}
		else if(e.getKeyCode() == KeyEvent.VK_ENTER)
		{
			if(cursorRow == 6 && cursorCol == 0)
				AsteroidsGame.game.endOptions(needNew, false); // stop optioning
			else if(cursorRow == 6 && cursorCol == 1){
				AsteroidsGame.game.saveGame(); // save game
			}
			else if(cursorRow == 6 && cursorCol == 2)
			{
				AsteroidsGame.game.loadGame(); // load game
				// the game will end options on its own after loading
			}
			else if(cursorRow == 6 && cursorCol == 3)
				AsteroidsGame.game.endOptions(false, true); // quit game
			else if(cursorRow == 7)
				AsteroidsGame.game.resetScoreFile();
		}
		else if(e.getKeyCode() == KeyEvent.VK_ESCAPE)
		{
			AsteroidsGame.game.endOptions(needNew, false); // same as continue option
		}
	}
	
	// x and y are top LEFT of location
	// returns width of name
	private int drawOptionName(int x, int y, String str, Graphics g)
	{
		Graphics2D g1 = (Graphics2D) g.create(); // make a copy of the graphics so we can transform it
		g1.translate(x,y);
		
		int height = g1.getFontMetrics(nameFont).getHeight();
		int width = g1.getFontMetrics(nameFont).stringWidth(str);
		
		g1.setFont(nameFont);
		g1.drawString(str, 0, height);
		
		return width;
	}
	
	// x and y are top LEFT of location
	// returns width of name
	private int drawOption(int x, int y, String str, boolean selected, Graphics g)
	{
		Graphics2D g1 = (Graphics2D) g.create(); // make a copy of the graphics so we can transform it
		g1.translate(x,y);
		
		Font font = selected? selectedFont : optionFont;
		
		// the size depends on the largest relevant font, not necesarily
		//  the font actually being used
		int height = g1.getFontMetrics(nameFont).getHeight();
		int width = g1.getFontMetrics(selectedFont).stringWidth(str);
		
		g1.setFont(font);
		g1.drawString(str, 0, height);
		
		return width;
	}
	
	private int drawText(int x, int y, String str, Graphics g)
	{
		Graphics2D g1 = (Graphics2D) g.create();
		g1.translate(x,y);
		
		int height = g1.getFontMetrics(textFont).getHeight();
		int width = g1.getFontMetrics(textFont).stringWidth(str);
		
		g1.setFont(textFont);
		g1.drawString(str, 0, height);
		
		return width;
	}
}
